package com.misty.java8;


import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.*;

//参考链接 ：https://www.jianshu.com/p/0bb4daf6c800
public class Demo {
    public static List<Person> persons = new ArrayList<Person>();

    static {
        persons.add(new Person("jack", 30));
        persons.add(new Person("mike", 25));
        persons.add(new Person("tom", 1));
        persons.add(new Person("imist", 41));
        persons.add(new Person("dagang1", 25));
        persons.add(new Person("xiaoqiang1", 52));
        persons.add(new Person("xiaolao", 15));
        persons.add(new Person("xiaolao1", 15));
        persons.add(new Person("xiaolao2", 15));
        persons.add(new Person("hong", 55));
        persons.add(new Person("john1", 75));
        persons.add(new Person("john12", 0));
        persons.add(new Person("xcv1", 40));
        persons.add(new Person("hgjk1", 25));
        persons.add(new Person("tom1", 1));
        persons.add(new Person("imist1", 18));
        persons.add(new Person("dagang", 25));
        persons.add(new Person("xiaoqiang", 75));
        persons.add(new Person("dsf", 123));
        persons.add(new Person("sdf", 15));
        persons.add(new Person("asdfas", 66));
        persons.add(new Person("asdf", 55));
        persons.add(new Person("jofhhn", 88));
        persons.add(new Person(" dsg", 10));
    }

    /**
     * 语法 : Person::getName 调用Persion类的getName方法
     * .collect(toList()) 相当于Collectors.toList()
     */

    @Test
    public void test1() {
        // 1.转换为stream 2.转置为String 3. 排序（按照集合元素实现的Comparable接口） 4. 限制长度为5 ，5. 转换为集合
        List<String> list = persons.stream().map(Person::getName).sorted().limit(5).collect(toList());
        System.out.println(list);
    }

    /**
     * 这里的stream是一次性的，和迭代器类似
     * java.lang.IllegalStateException: stream has already been operated upon or closed
     */
    @Test
    public void test2() {
        Stream<String> stream = persons.stream().map(Person::getName).sorted().limit(5);
        List<String> list = stream.collect(toList());
        List<String> list2 = stream.collect(toList());
    }

    /**
     * stream() / parallelStream() 集合转换成串行流/并行流
     */
    @Test
    public void test3() {
        Stream<Person> stream = persons.stream();
        Stream<Person> personStream = persons.parallelStream();
    }

    /**
     * filter(T -> boolean)
     * 保留返回为true的元素 ,可以使用方法
     */
    @Test
    public void test4() {
        List<Person> people = persons.stream()
                .filter(this::check)
                .collect(Collectors.toList());
        System.out.println(people);
    }

    public boolean check(Person person) {
        return person.getAge() < 50 && person.getName().equals("imist");
    }

    /**
     * distinct()
     * 去除重复元素，这个方法是通过类的 equals
     * 方法来判断两个元素是否相等的, 如果不定义好equal,自定义的类元素无法去重(本例定义只有当age和name相同才确定为相等)
     */
    @Test
    public void test5() {
        System.out.println("before:" + persons.size());
        List<Person> people = persons.stream()
                .distinct().collect(toList());
        System.out.println("after:" + people.size());
    }

    /**
     * sorted() / sorted((T, T) -> int)
     * 如果流中的元素的类实现了Comparable接口，可以直接调用sort()进行排序
     * sorted((T, T) -> int)
     * 或者  .sorted(Comparator.comparingInt(Person::getAge))
     */
    @Test
    public void test6() {
        List<Person> people = persons.parallelStream()
                //.sorted((p1,p2)-> p1.getAge() - p2.getAge()) //简化简化为
                .sorted(Comparator.comparingInt(Person::getAge))
                .collect(toList());
        System.out.println(people);
    }

    /**
     * .limit(long n ) 返回前n个元素
     * .skip(long n) 去除前个元素
     * 两者一起使用 执行逻辑取决于先后顺序
     */
    @Test
    public void test7() {
        List<Person> people = persons.stream()
                .limit(5) //返回前五个元素
                .skip(2)  //去除前两个元素
                .collect(toList());
        System.out.println(people);
    }

    /**
     * map()转置操作，将一个流中现有的元素转换成另一个元素类型
     */
    @Test
    public void test8() {
        List<String> people = persons.stream()
                .map(Person::getName)
                .collect(toList());
        System.out.println(people);
    }

    /**
     * flatMap(T -> Stream<R>)
     * 将流中的每一个元素 T 映射为一个流，再把每一个流连接成为一个流
     * <p>
     * 首先 map 方法分割每个字符串元素，但此时流的类型为 Stream<String[ ]>，因为 split 方法返回的是 String[] 类型；
     * 所以我们需要使用 flatMap 方法，先使用Arrays::stream将每个 String[ ] 元素变成一个 Stream<String> 流，
     * 然后 flatMap 会将每一个流连接成为一个流，最终返回我们需要的 Stream<String>
     */
    @Test
    public void test9() {
        List<String> list = new ArrayList<>();
        list.add("aaa bbb ccc");
        list.add("ddd eee fff");
        list.add("ggg hhh iii");
        List<String> list2 = list.stream().map(s -> s.split(" ")).flatMap(Arrays::stream).collect(toList());
        System.out.println(list2);
    }

    /**
     * anyMatch(T -> boolean) 流中是否有一个元素匹配给定的 T -> boolean 条件
     * allMatch(T -> boolean)  流中是否所有元素都匹配给定的 T -> boolean 条件
     * noneMatch(T -> boolean)
     */
    @Test
    public void test10() {
        boolean isAnyMatch = persons.stream().anyMatch(person -> person.getName().equals("imist"));
        boolean isAllMatch = persons.stream().allMatch(person -> person.getName().equals("imist"));
        boolean isNoneMatch = persons.stream().noneMatch(person -> person.getName().equals("imist"));
        System.out.println(isAnyMatch);
        System.out.println(isAllMatch);
        System.out.println(isNoneMatch);
    }

    /**
     * findAny() 和 findFirst()
     * findAny()：找到其中一个元素 （使用 stream() 时找到的是第一个元素；使用 parallelStream() 并行时找到的是其中一个元素）
     * findFirst()：找到第一个元素
     */
    @Test
    public void test11() {
        Person person = persons.stream().findAny().get();
        Person person2 = persons.parallelStream().findAny().get();
        Person person3 = persons.stream().findFirst().get();
        System.out.println(person);
        System.out.println(person2);
        System.out.println(person3);
    }

    /**
     * reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)
     * 用于组合流中的元素，如求和，求积，求最大值等
     * reduce 第一个参数 0 代表起始值为 0，lambda (a, b) -> a + b 即将两值相加产生一个新值
     */
    @Test
    public void test12() {
        int sum = persons.stream().map(Person::getAge).reduce(0, (p1, p2) -> p1 + p2);
        //相当于
        int sum2 = persons.stream().map(Person::getAge).reduce(0, Integer::sum);
        //即不接受任何起始值，但因为没有初始值，需要考虑结果可能不存在的情况，因此返回的是 Optional 类型
        Optional<Integer> sum3 = persons.stream().map(Person::getAge).reduce(Integer::sum);
        Integer sum4 = sum3.get();
        System.out.println(sum + "---" + sum2);
        System.out.println(sum3 + "---" + sum4);

    }

    /* count()
     返回流中元素个数，结果为long 类型
     */
    @Test
    public void test13() {
        long count = persons.stream().count();
        System.out.println(count);
    }

    /* collect()
    收集方法，我们很常用的是 collect(toList())，当然还有 collect(toSet()) 等，参数是一个收集器接口，这个后面会另外讲*/
    @Test
    public void test14() {
        List<Person> people = persons.stream().limit(10).collect(toList());
        //如果 key 的值重复了可能会报错
        Map<String, Person> map = persons.stream().collect(toMap(Person::getName, p -> p));
        //groupingBy 分组
        Map<Integer, List<Person>> group = persons.stream().collect(groupingBy(Person::getAge));
        //等同于
        Map<Integer, List<Person>> group2 = persons.stream().collect(groupingBy(Person::getAge, toList()));
        // partitioningBy  分区，分区与分组的区别在于，分区是按照 true 和 false 来分的，因此partitioningBy 接受的参数的 lambda 也是 T -> boolean
        Map<Boolean, List<Person>> partition = persons.stream()
                .collect(partitioningBy(p -> p.getAge() <= 20));
    }

    /**
     * forEach()
     */
    @Test
    public void test15() {
        persons.forEach(System.out::println);

        persons.forEach(person -> {
            System.out.println(person.getName()+"的年龄是"+person.getAge());
        });
    }

    /**
     * unordered()还有这个比较不起眼的方法，返回一个等效的无序流，当然如果流本身就是无序的话，那可能就会直接返回其本身
     * 摘录于Java8文档 https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#Ordering
     *
     * 对于顺序流，顺序的存在与否不会影响性能，只影响确定性。如果流是顺序的，则在相同的源上重复执行相同的流管道将产生相同的结果;
     * 如果是非顺序流，重复执行可能会产生不同的结果。 对于并行流，放宽排序约束有时可以实现更高效的执行。 ...
     * 在流有序时, 但用户不特别关心该顺序的情况下，使用 unordered 明确地对流进行去除有序约束可以改善某些有状态或终端操作的并行性能。
     */

    @Test
    public void test16(){
        List<Person> personList = persons.stream().collect(toList());
        System.out.println("有序集合");
        personList.forEach(System.out::println);

        //结果不会乱序,开始还以为是collect(toList())收集器作祟导致不会乱序
        System.out.println("串行流无序");
        persons.stream().unordered().forEach(System.out::println);

        //达到了乱序效果
        System.out.println("并行流无序");
        persons.stream().parallel().unordered().forEach(System.out::println);


    }


}
